This R Markdown documents the data cleaning analysis carried out on the dataset.

Add a new chunk by clicking the *Insert Chunk* button on the toolbar or by pressing *Ctrl+Alt+I*.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the *Preview* button or press *Ctrl+Shift+K* to preview the HTML file).

The preview shows you a rendered HTML copy of the contents of the editor. Consequently, unlike *Knit*, *Preview* does not run any R code chunks. Instead, the output of the chunk when it was last run in the editor is displayed.

Data Cleaning

In carrying out data cleaning, a few key variables come to mind:

Uniqueness of Players

# the number of rows of the data for league is consistent
map_dbl(list(league_defense, league_shooting, league_passing, 
             league_goalkeeping), nrow)
[1] 5554 5554 5554  413
# However, the number of players in tournaments exhibit inconsistencies
map_dbl(list(tournament_defense, tournament_shooting, tournament_passing, 
             tournament_goalkeeping), nrow)%>%
  set_names(c("tournament_defense","tournament_shooting",
             "tournament_passing","tournament_goalkeeping"))
    tournament_defense    tournament_shooting     tournament_passing 
                   488                   2015                    488 
tournament_goalkeeping 
                   129 

The next step is to ensure that the distinct players are the same. We first try the league players. We can confirm that the list of shooting, passing and defense players are indeed correct.

check_list_equal<- function(out, input){
  if(!all(out==input)){
    return(done(FALSE))
  }
  input
}

# If output is not consistent
list(2,2,3,4)%>%
  reduce(check_list_equal)
[1] FALSE
# There are 3429 league players that are consistent. 
league_players<-list(league_defense, league_shooting, league_passing)%>%
  map(function(db) db%>% 
      distinct(Player)%>%
      arrange(Player))%>%
  reduce(check_list_equal)
league_players

Tournament player data cleaning is slightly more complicated, mainly as shooting and goalkeeping data is available for 2020 while the other data sets only have 2021 data.

map(list(tournament_defense, tournament_shooting, tournament_passing, 
             tournament_goalkeeping), 
        ~.[["Year"]]%>%
          unique())%>%
  set_names(c("tournament_defense","tournament_shooting",
             "tournament_passing","tournament_goalkeeping"))
$tournament_defense
[1] 2021

$tournament_shooting
[1] 2020 2021

$tournament_passing
[1] 2021

$tournament_goalkeeping
[1] 2020 2021

First check that the defense and passing have the same players. followed by checking if shooting is indeed a subset of the larger player pool.

# For tournament
tournament_players<-list(tournament_defense, tournament_passing)%>%
  map(function(db) db%>% 
      distinct(Player, Year, Nation)%>%
      arrange(Player,Year,Nation))%>%
  reduce(check_list_equal)

check_player_pool<-function(big_pool, comparator,
                            pool_col="Player", comparator_col="Player"){
  big_pool<-distinct(big_pool, across(matches(pool_col)))
  comparator<-distinct(comparator, across(matches(comparator_col)))
  
  if(all(comparator[[comparator_col]]%in% big_pool[[pool_col]])){
    message("All tournament shooting players are in the player pool.")
  }
  else{
    warning("Not all tournament shooting players are in the player pool.")
    print(
      paste("Of the", nrow(comparator), "Players, only",
          sum(comparator[[comparator_col]]%in% big_pool[[pool_col]]),
          "are in the bigger pool."))
    comparator[!comparator[[comparator_col]]%in% big_pool[[pool_col]],]
  }
}

check_player_pool(tournament_players, 
                  tournament_shooting%>%
                    filter(Year==2021))
All tournament shooting players are in the player pool.

Age Issue

A separate issue observed is that ages in the tournaments are not consistent:

# Does age increase over time?
# Confusingly, it decreases over time.
tournament_shooting%>%
  group_by(Player)%>%
  mutate(occurance = n())%>%
  filter(occurance>1)%>%
  arrange(Player,Year, Age)%>%
  select(Player,Year, Age)

tournament_shooting%>%
  group_by(Player)%>%
  mutate(occurance = n())%>%
  filter(occurance>1)%>%
  arrange(Player,Year, Age)%>%
  pivot_wider(id_cols = Player,names_from = Year, values_from = Age,
              names_prefix = "year_")%>%
  mutate(diff = year_2021 - year_2020)%>%
  group_by(diff)%>%
  summarise(count = n())

tournament_goalkeeping%>%
  group_by(Player)%>%
  mutate(occurance = n())%>%
  filter(occurance>1)%>%
  arrange(Player,Year, Age)%>%
  select(Player,Year, Age)

This is not the case for league data, however.

map(list(league_defense, league_shooting, league_passing, 
             league_goalkeeping), 
    function(db) {
      db%>%
        group_by(Player)%>%
        mutate(occurance = max(Year)-min(Year))%>%
        filter(occurance>=1)%>%
        select(Player,Year, Age)%>%
        ungroup()%>%
        arrange(Player,Year, desc(Age))%>%
        distinct()%>%
        pivot_wider(names_from = Year, values_from = Age,
                    names_prefix = "year_")%>%
        mutate(diff = year_2021 - year_2020)%>%
        group_by(diff)%>%
        summarise(count = n())
    })
[[1]]

[[2]]

[[3]]

[[4]]
NA

Compare the leagued data with the tournament data:

map(list(tournament_shooting, tournament_goalkeeping), 
    function(db) {
      db%>%
        group_by(Player)%>%
        mutate(occurance = max(Year)-min(Year))%>%
        filter(occurance>=1)%>%
        select(Player,Year, Age)%>%
        ungroup()%>%
        arrange(Player,Year, desc(Age))%>%
        distinct()%>%
        pivot_wider(names_from = Year, values_from = Age,
                    names_prefix = "year_")%>%
        mutate(diff = year_2021 - year_2020)%>%
        group_by(diff)%>%
        summarise(count = n())
    })%>%
  set_names(c("tournament_shooting","tournament_goalkeeping"))
$tournament_shooting

$tournament_goalkeeping
NA

Find the average age difference between calculated and the actual age.

tournament_shooting%>%
  mutate(calc_ages=Year - Born)%>%
  mutate(diff = calc_ages - Age)%>%
  group_by(diff)%>%
  summarise(count =n())

league_shooting%>%
  mutate(calc_ages=Year - Born)%>%
  mutate(diff = calc_ages - Age)%>%
  group_by(diff)%>%
  summarise(count =n())

The assumption used here is that the age should be standardized to year less birth year. This ensures consistencies between the data set. League ages tend to underestimate the true age, while tournament ages tend to overestimate the ages.

tournament_shooting%>%
  select(Player, Year, Age)%>%
  rename(age_tourny=Age)%>%
  filter(Player %in% league_players$Player)%>%
  inner_join(league_defense%>%
              arrange(Player, Year, Age)%>%
              distinct(Player, Year, Age),
            by = c("Player", "Year"))%>%
  mutate(diff = age_tourny - Age)%>%
  group_by(diff)%>%
  summarise(count = n())
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoDQogIGVjaG8gPSBUUlVFDQopDQpzb3VyY2UoIjAwIHNldCB1cC5SIikNCmBgYA0KDQpUaGlzIFIgTWFya2Rvd24gZG9jdW1lbnRzIHRoZSBkYXRhIGNsZWFuaW5nIGFuYWx5c2lzIGNhcnJpZWQgb3V0IG9uIHRoZSBkYXRhc2V0Lg0KDQpgYGANCkFkZCBhIG5ldyBjaHVuayBieSBjbGlja2luZyB0aGUgKkluc2VydCBDaHVuayogYnV0dG9uIG9uIHRoZSB0b29sYmFyIG9yIGJ5IHByZXNzaW5nICpDdHJsK0FsdCtJKi4NCg0KV2hlbiB5b3Ugc2F2ZSB0aGUgbm90ZWJvb2ssIGFuIEhUTUwgZmlsZSBjb250YWluaW5nIHRoZSBjb2RlIGFuZCBvdXRwdXQgd2lsbCBiZSBzYXZlZCBhbG9uZ3NpZGUgaXQgKGNsaWNrIHRoZSAqUHJldmlldyogYnV0dG9uIG9yIHByZXNzICpDdHJsK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuDQoNClRoZSBwcmV2aWV3IHNob3dzIHlvdSBhIHJlbmRlcmVkIEhUTUwgY29weSBvZiB0aGUgY29udGVudHMgb2YgdGhlIGVkaXRvci4gQ29uc2VxdWVudGx5LCB1bmxpa2UgKktuaXQqLCAqUHJldmlldyogZG9lcyBub3QgcnVuIGFueSBSIGNvZGUgY2h1bmtzLiBJbnN0ZWFkLCB0aGUgb3V0cHV0IG9mIHRoZSBjaHVuayB3aGVuIGl0IHdhcyBsYXN0IHJ1biBpbiB0aGUgZWRpdG9yIGlzIGRpc3BsYXllZC4NCmBgYA0KDQojIyBEYXRhIENsZWFuaW5nDQpJbiBjYXJyeWluZyBvdXQgZGF0YSBjbGVhbmluZywgYSBmZXcga2V5IHZhcmlhYmxlcyBjb21lIHRvIG1pbmQ6DQoNCiogVW5pcXVlbmVzcyBvZiBQbGF5ZXJzDQoqIEFnZQ0KKiBDb25zaXN0ZW5jeSBpbiBuYXRpb25hbGl0eQ0KDQojIyMgVW5pcXVlbmVzcyBvZiBQbGF5ZXJzDQpgYGB7ciwgZWNobyA9IFR9DQojIHRoZSBudW1iZXIgb2Ygcm93cyBvZiB0aGUgZGF0YSBmb3IgbGVhZ3VlIGlzIGNvbnNpc3RlbnQNCm1hcF9kYmwobGlzdChsZWFndWVfZGVmZW5zZSwgbGVhZ3VlX3Nob290aW5nLCBsZWFndWVfcGFzc2luZywgDQogICAgICAgICAgICAgbGVhZ3VlX2dvYWxrZWVwaW5nKSwgbnJvdykNCg0KIyBIb3dldmVyLCB0aGUgbnVtYmVyIG9mIHBsYXllcnMgaW4gdG91cm5hbWVudHMgZXhoaWJpdCBpbmNvbnNpc3RlbmNpZXMNCm1hcF9kYmwobGlzdCh0b3VybmFtZW50X2RlZmVuc2UsIHRvdXJuYW1lbnRfc2hvb3RpbmcsIHRvdXJuYW1lbnRfcGFzc2luZywgDQogICAgICAgICAgICAgdG91cm5hbWVudF9nb2Fsa2VlcGluZyksIG5yb3cpJT4lDQogIHNldF9uYW1lcyhjKCJ0b3VybmFtZW50X2RlZmVuc2UiLCJ0b3VybmFtZW50X3Nob290aW5nIiwNCiAgICAgICAgICAgICAidG91cm5hbWVudF9wYXNzaW5nIiwidG91cm5hbWVudF9nb2Fsa2VlcGluZyIpKQ0KYGBgDQoNClRoZSBuZXh0IHN0ZXAgaXMgdG8gZW5zdXJlIHRoYXQgdGhlIGRpc3RpbmN0IHBsYXllcnMgYXJlIHRoZSBzYW1lLiBXZSBmaXJzdCB0cnkgdGhlIGxlYWd1ZSBwbGF5ZXJzLiBXZSBjYW4gY29uZmlybSB0aGF0IHRoZSBsaXN0IG9mIHNob290aW5nLCBwYXNzaW5nIGFuZCBkZWZlbnNlIHBsYXllcnMgYXJlIGluZGVlZCBjb3JyZWN0Lg0KDQpgYGB7ciwgZWNobyA9IFR9DQpjaGVja19saXN0X2VxdWFsPC0gZnVuY3Rpb24ob3V0LCBpbnB1dCl7DQogIGlmKCFhbGwob3V0PT1pbnB1dCkpew0KICAgIHJldHVybihkb25lKEZBTFNFKSkNCiAgfQ0KICBpbnB1dA0KfQ0KDQojIElmIG91dHB1dCBpcyBub3QgY29uc2lzdGVudA0KbGlzdCgyLDIsMyw0KSU+JQ0KICByZWR1Y2UoY2hlY2tfbGlzdF9lcXVhbCkNCg0KIyBUaGVyZSBhcmUgMzQyOSBsZWFndWUgcGxheWVycyB0aGF0IGFyZSBjb25zaXN0ZW50LiANCmxlYWd1ZV9wbGF5ZXJzPC1saXN0KGxlYWd1ZV9kZWZlbnNlLCBsZWFndWVfc2hvb3RpbmcsIGxlYWd1ZV9wYXNzaW5nKSU+JQ0KICBtYXAoZnVuY3Rpb24oZGIpIGRiJT4lIA0KICAgICAgZGlzdGluY3QoUGxheWVyKSU+JQ0KICAgICAgYXJyYW5nZShQbGF5ZXIpKSU+JQ0KICByZWR1Y2UoY2hlY2tfbGlzdF9lcXVhbCkNCmxlYWd1ZV9wbGF5ZXJzDQpgYGANCg0KVG91cm5hbWVudCBwbGF5ZXIgZGF0YSBjbGVhbmluZyBpcyBzbGlnaHRseSBtb3JlIGNvbXBsaWNhdGVkLCBtYWlubHkgYXMgc2hvb3RpbmcgYW5kIGdvYWxrZWVwaW5nIGRhdGEgaXMgYXZhaWxhYmxlIGZvciAyMDIwIHdoaWxlIHRoZSBvdGhlciBkYXRhIHNldHMgb25seSBoYXZlIDIwMjEgZGF0YS4NCg0KYGBge3J9DQptYXAobGlzdCh0b3VybmFtZW50X2RlZmVuc2UsIHRvdXJuYW1lbnRfc2hvb3RpbmcsIHRvdXJuYW1lbnRfcGFzc2luZywgDQogICAgICAgICAgICAgdG91cm5hbWVudF9nb2Fsa2VlcGluZyksIA0KICAgICAgICB+LltbIlllYXIiXV0lPiUNCiAgICAgICAgICB1bmlxdWUoKSklPiUNCiAgc2V0X25hbWVzKGMoInRvdXJuYW1lbnRfZGVmZW5zZSIsInRvdXJuYW1lbnRfc2hvb3RpbmciLA0KICAgICAgICAgICAgICJ0b3VybmFtZW50X3Bhc3NpbmciLCJ0b3VybmFtZW50X2dvYWxrZWVwaW5nIikpDQpgYGANCg0KRmlyc3QgY2hlY2sgdGhhdCB0aGUgZGVmZW5zZSBhbmQgcGFzc2luZyBoYXZlIHRoZSBzYW1lIHBsYXllcnMuICBmb2xsb3dlZCBieSBjaGVja2luZyBpZiBzaG9vdGluZyBpcyBpbmRlZWQgYSBzdWJzZXQgb2YgdGhlIGxhcmdlciBwbGF5ZXIgcG9vbC4NCg0KYGBge3J9DQojIEZvciB0b3VybmFtZW50DQp0b3VybmFtZW50X3BsYXllcnM8LWxpc3QodG91cm5hbWVudF9kZWZlbnNlLCB0b3VybmFtZW50X3Bhc3NpbmcpJT4lDQogIG1hcChmdW5jdGlvbihkYikgZGIlPiUgDQogICAgICBkaXN0aW5jdChQbGF5ZXIsIFllYXIsIE5hdGlvbiklPiUNCiAgICAgIGFycmFuZ2UoUGxheWVyLFllYXIsTmF0aW9uKSklPiUNCiAgcmVkdWNlKGNoZWNrX2xpc3RfZXF1YWwpDQoNCmNoZWNrX3BsYXllcl9wb29sPC1mdW5jdGlvbihiaWdfcG9vbCwgY29tcGFyYXRvciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwb29sX2NvbD0iUGxheWVyIiwgY29tcGFyYXRvcl9jb2w9IlBsYXllciIpew0KICBiaWdfcG9vbDwtZGlzdGluY3QoYmlnX3Bvb2wsIGFjcm9zcyhtYXRjaGVzKHBvb2xfY29sKSkpDQogIGNvbXBhcmF0b3I8LWRpc3RpbmN0KGNvbXBhcmF0b3IsIGFjcm9zcyhtYXRjaGVzKGNvbXBhcmF0b3JfY29sKSkpDQogIA0KICBpZihhbGwoY29tcGFyYXRvcltbY29tcGFyYXRvcl9jb2xdXSVpbiUgYmlnX3Bvb2xbW3Bvb2xfY29sXV0pKXsNCiAgICBtZXNzYWdlKCJBbGwgdG91cm5hbWVudCBzaG9vdGluZyBwbGF5ZXJzIGFyZSBpbiB0aGUgcGxheWVyIHBvb2wuIikNCiAgfQ0KICBlbHNlew0KICAgIHdhcm5pbmcoIk5vdCBhbGwgdG91cm5hbWVudCBzaG9vdGluZyBwbGF5ZXJzIGFyZSBpbiB0aGUgcGxheWVyIHBvb2wuIikNCiAgICBwcmludCgNCiAgICAgIHBhc3RlKCJPZiB0aGUiLCBucm93KGNvbXBhcmF0b3IpLCAiUGxheWVycywgb25seSIsDQogICAgICAgICAgc3VtKGNvbXBhcmF0b3JbW2NvbXBhcmF0b3JfY29sXV0laW4lIGJpZ19wb29sW1twb29sX2NvbF1dKSwNCiAgICAgICAgICAiYXJlIGluIHRoZSBiaWdnZXIgcG9vbC4iKSkNCiAgICBjb21wYXJhdG9yWyFjb21wYXJhdG9yW1tjb21wYXJhdG9yX2NvbF1dJWluJSBiaWdfcG9vbFtbcG9vbF9jb2xdXSxdDQogIH0NCn0NCg0KY2hlY2tfcGxheWVyX3Bvb2wodG91cm5hbWVudF9wbGF5ZXJzLCANCiAgICAgICAgICAgICAgICAgIHRvdXJuYW1lbnRfc2hvb3RpbmclPiUNCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKFllYXI9PTIwMjEpKQ0KDQpgYGANCg0KIyMjIEFnZSBJc3N1ZQ0KQSBzZXBhcmF0ZSBpc3N1ZSBvYnNlcnZlZCBpcyB0aGF0IGFnZXMgaW4gdGhlIHRvdXJuYW1lbnRzIGFyZSBub3QgY29uc2lzdGVudDoNCg0KYGBge3IgdG91cm5hbWVudF9hZ2VfaXNzdWVzfQ0KIyBEb2VzIGFnZSBpbmNyZWFzZSBvdmVyIHRpbWU/DQojIENvbmZ1c2luZ2x5LCBpdCBkZWNyZWFzZXMgb3ZlciB0aW1lLg0KdG91cm5hbWVudF9zaG9vdGluZyU+JQ0KICBncm91cF9ieShQbGF5ZXIpJT4lDQogIG11dGF0ZShvY2N1cmFuY2UgPSBuKCkpJT4lDQogIGZpbHRlcihvY2N1cmFuY2U+MSklPiUNCiAgYXJyYW5nZShQbGF5ZXIsWWVhciwgQWdlKSU+JQ0KICBzZWxlY3QoUGxheWVyLFllYXIsIEFnZSkNCg0KdG91cm5hbWVudF9zaG9vdGluZyU+JQ0KICBncm91cF9ieShQbGF5ZXIpJT4lDQogIG11dGF0ZShvY2N1cmFuY2UgPSBuKCkpJT4lDQogIGZpbHRlcihvY2N1cmFuY2U+MSklPiUNCiAgYXJyYW5nZShQbGF5ZXIsWWVhciwgQWdlKSU+JQ0KICBwaXZvdF93aWRlcihpZF9jb2xzID0gUGxheWVyLG5hbWVzX2Zyb20gPSBZZWFyLCB2YWx1ZXNfZnJvbSA9IEFnZSwNCiAgICAgICAgICAgICAgbmFtZXNfcHJlZml4ID0gInllYXJfIiklPiUNCiAgbXV0YXRlKGRpZmYgPSB5ZWFyXzIwMjEgLSB5ZWFyXzIwMjApJT4lDQogIGdyb3VwX2J5KGRpZmYpJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSkNCg0KdG91cm5hbWVudF9nb2Fsa2VlcGluZyU+JQ0KICBncm91cF9ieShQbGF5ZXIpJT4lDQogIG11dGF0ZShvY2N1cmFuY2UgPSBuKCkpJT4lDQogIGZpbHRlcihvY2N1cmFuY2U+MSklPiUNCiAgYXJyYW5nZShQbGF5ZXIsWWVhciwgQWdlKSU+JQ0KICBzZWxlY3QoUGxheWVyLFllYXIsIEFnZSkNCmBgYA0KDQpUaGlzIGlzIG5vdCB0aGUgY2FzZSBmb3IgbGVhZ3VlIGRhdGEsIGhvd2V2ZXIuDQoNCmBgYHtyIGxlYWd1ZV9hZ2Vfc3VtbWFyeX0NCm1hcChsaXN0KGxlYWd1ZV9kZWZlbnNlLCBsZWFndWVfc2hvb3RpbmcsIGxlYWd1ZV9wYXNzaW5nLCANCiAgICAgICAgICAgICBsZWFndWVfZ29hbGtlZXBpbmcpLCANCiAgICBmdW5jdGlvbihkYikgew0KICAgICAgZGIlPiUNCiAgICAgICAgZ3JvdXBfYnkoUGxheWVyKSU+JQ0KICAgICAgICBtdXRhdGUob2NjdXJhbmNlID0gbWF4KFllYXIpLW1pbihZZWFyKSklPiUNCiAgICAgICAgZmlsdGVyKG9jY3VyYW5jZT49MSklPiUNCiAgICAgICAgc2VsZWN0KFBsYXllcixZZWFyLCBBZ2UpJT4lDQogICAgICAgIHVuZ3JvdXAoKSU+JQ0KICAgICAgICBhcnJhbmdlKFBsYXllcixZZWFyLCBkZXNjKEFnZSkpJT4lDQogICAgICAgIGRpc3RpbmN0KCklPiUNCiAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gQWdlLA0KICAgICAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAieWVhcl8iKSU+JQ0KICAgICAgICBtdXRhdGUoZGlmZiA9IHllYXJfMjAyMSAtIHllYXJfMjAyMCklPiUNCiAgICAgICAgZ3JvdXBfYnkoZGlmZiklPiUNCiAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KICAgIH0pDQpgYGANCg0KQ29tcGFyZSB0aGUgbGVhZ3VlZCBkYXRhIHdpdGggdGhlIHRvdXJuYW1lbnQgZGF0YToNCg0KYGBge3IgdG91cm5hbWVudF9hZ2Vfc3VtbWFyeX0NCm1hcChsaXN0KHRvdXJuYW1lbnRfc2hvb3RpbmcsIHRvdXJuYW1lbnRfZ29hbGtlZXBpbmcpLCANCiAgICBmdW5jdGlvbihkYikgew0KICAgICAgZGIlPiUNCiAgICAgICAgZ3JvdXBfYnkoUGxheWVyKSU+JQ0KICAgICAgICBtdXRhdGUob2NjdXJhbmNlID0gbWF4KFllYXIpLW1pbihZZWFyKSklPiUNCiAgICAgICAgZmlsdGVyKG9jY3VyYW5jZT49MSklPiUNCiAgICAgICAgc2VsZWN0KFBsYXllcixZZWFyLCBBZ2UpJT4lDQogICAgICAgIHVuZ3JvdXAoKSU+JQ0KICAgICAgICBhcnJhbmdlKFBsYXllcixZZWFyLCBkZXNjKEFnZSkpJT4lDQogICAgICAgIGRpc3RpbmN0KCklPiUNCiAgICAgICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IFllYXIsIHZhbHVlc19mcm9tID0gQWdlLA0KICAgICAgICAgICAgICAgICAgICBuYW1lc19wcmVmaXggPSAieWVhcl8iKSU+JQ0KICAgICAgICBtdXRhdGUoZGlmZiA9IHllYXJfMjAyMSAtIHllYXJfMjAyMCklPiUNCiAgICAgICAgZ3JvdXBfYnkoZGlmZiklPiUNCiAgICAgICAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KICAgIH0pJT4lDQogIHNldF9uYW1lcyhjKCJ0b3VybmFtZW50X3Nob290aW5nIiwidG91cm5hbWVudF9nb2Fsa2VlcGluZyIpKQ0KYGBgDQoNCkZpbmQgdGhlIGF2ZXJhZ2UgYWdlIGRpZmZlcmVuY2UgYmV0d2VlbiBjYWxjdWxhdGVkIGFuZCB0aGUgYWN0dWFsIGFnZS4NCg0KYGBge3J9DQp0b3VybmFtZW50X3Nob290aW5nJT4lDQogIG11dGF0ZShjYWxjX2FnZXM9WWVhciAtIEJvcm4pJT4lDQogIG11dGF0ZShkaWZmID0gY2FsY19hZ2VzIC0gQWdlKSU+JQ0KICBncm91cF9ieShkaWZmKSU+JQ0KICBzdW1tYXJpc2UoY291bnQgPW4oKSkNCg0KbGVhZ3VlX3Nob290aW5nJT4lDQogIG11dGF0ZShjYWxjX2FnZXM9WWVhciAtIEJvcm4pJT4lDQogIG11dGF0ZShkaWZmID0gY2FsY19hZ2VzIC0gQWdlKSU+JQ0KICBncm91cF9ieShkaWZmKSU+JQ0KICBzdW1tYXJpc2UoY291bnQgPW4oKSkNCmBgYA0KDQpUaGUgYXNzdW1wdGlvbiB1c2VkIGhlcmUgaXMgdGhhdCB0aGUgYWdlIHNob3VsZCBiZSBzdGFuZGFyZGl6ZWQgdG8geWVhciBsZXNzIGJpcnRoIHllYXIuIFRoaXMgZW5zdXJlcyBjb25zaXN0ZW5jaWVzIGJldHdlZW4gdGhlIGRhdGEgc2V0LiBMZWFndWUgYWdlcyB0ZW5kIHRvIHVuZGVyZXN0aW1hdGUgdGhlIHRydWUgYWdlLCB3aGlsZSB0b3VybmFtZW50IGFnZXMgdGVuZCB0byBvdmVyZXN0aW1hdGUgdGhlIGFnZXMuDQoNCmBgYHtyfQ0KdG91cm5hbWVudF9zaG9vdGluZyU+JQ0KICBzZWxlY3QoUGxheWVyLCBZZWFyLCBBZ2UpJT4lDQogIHJlbmFtZShhZ2VfdG91cm55PUFnZSklPiUNCiAgZmlsdGVyKFBsYXllciAlaW4lIGxlYWd1ZV9wbGF5ZXJzJFBsYXllciklPiUNCiAgaW5uZXJfam9pbihsZWFndWVfZGVmZW5zZSU+JQ0KICAgICAgICAgICAgICBhcnJhbmdlKFBsYXllciwgWWVhciwgQWdlKSU+JQ0KICAgICAgICAgICAgICBkaXN0aW5jdChQbGF5ZXIsIFllYXIsIEFnZSksDQogICAgICAgICAgICBieSA9IGMoIlBsYXllciIsICJZZWFyIikpJT4lDQogIG11dGF0ZShkaWZmID0gYWdlX3RvdXJueSAtIEFnZSklPiUNCiAgZ3JvdXBfYnkoZGlmZiklPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKQ0KYGBgDQoNCg==